VI.FUME
在正式進入今天的主題前,我想要先把FUME的幾個特性跟大家講清楚,連同前面稍微提到的部分一起整合一下
特性1
如果這個輸入端的屬性項能夠從輸入中被找到,
#INPUT:
{
"mrn" : "12345"
}
#FUME
* identifier
* value = mrn
FHIR輸出端就會建立
#FHIR:
"identifier": [
{
"value": "12345"
}
]
該原則也同樣適用於複合資料結構,但反之,以該例子來說若"mrn"不存在,則不會進行FHIR轉換
特性2
FUME中若遇到相同的slice,永遠只會輸出最下面一行的內容:
#FUME
InstanceOf: Observation
* id = "123"
* valueString = "1212"
* valueInteger = 2
#FHIR
{
"resourceType": "Observation",
"id": "123",
"valueInteger": 2
}
因valueInteger與valueString同樣都是value[x]的slices,所以在同時擁有同屬性的元素時,要特別注意,這其實會帶來不少困擾
所以最下方的無論是變數集合還是FLASH,都應該要被妥善打包,這個後面實務的部分會再講清楚一些。
特性3
在FUME Mapping的欄位中使用$,會將輸入全數映射至FHIR輸出端
特性4
在FUME與JSONata中,變數內部可以包含JSON格式,也可以包含FLASH格式
#FUME
$claim :=
(
InstanceOf: Claim
* id = claim_id
* meta
* profile = "https://nhicore.nhi.gov.tw/pas/StructureDefinition/Claim-twpas"
)
特性5
在變數中的變數們彼此分隔要使用分號; 但若是變數在最外層擁有超過一個時,會報錯
#FUME
$result := (
$a := "HI";
$b := $now()
)
↑合法,但特性2告訴我們,FHIR的輸出結果僅有$b := $now()
#FUME
$a := "HI"
$b := $now()
↑不合法,無論加不加分號都回報Syntax error
補充 變數組合法:
#FUME
$result := (
$a := "HI";
$b := $now();
[$a,$b]
)
↑將兩個變數以array方式包裝,輸出為:
#FHIR
[
"HI",
"2025-08-15T07:41:05.062Z"
]
這樣的作法是符合只輸出最後一行的特性,只是在輸出前必須要先完成$a與$b,
邏輯上可以推廣到變數夾帶多個Resource,也就是Bundle.entry
特性6
FUME FLASH的左側對應輸入中,純數字為數字而非輸入屬性項名稱,
舉例:
#FUME
* identifier
* system = $urn
* value = 5 * 2
#INPUT
"5": 8
#FHIR
"identifier": [
{
"system": "urn:ietf:rfc:3986",
"value": "10"
}
]
純數字的輸入變數因無法兼容於JSONata的int variable,在左側執行時會預設其為數字而非輸入屬性項
特性7
所有FHIR Resource的開頭必然為
#FUME
InstanceOf:ResourceType
若有FUME FLASH語法不包含於變數內,會回報Syntax錯誤
原因是宣告ResourceType時,FUME就會自動幫你檢測這個Resource內應有的屬性項欄位
--
前兩天把FUME FLASH大致上談過一次了,首先可以最簡單的理解就是
#FUME
* (FHIR屬性項) = (輸入項)
輸入項本身就是完全提供JSONata嵌入的地方,可以執行任何JSONata語法,
而FUME本身可以認定是JSONata的超集,因為其提供了與FHIR Server協作等實作語法
1.條件式判斷(if else):
#FUME
* (FHIR屬性項) = (條件式) ? (TRUE) : (FALSE)
這個是最簡單的Conditional Statement,但他能夠推導成else if的模式
#FUME
* display = claim_subType_code = "1" ? "送核" : claim_subType_code = "2" ? "送核補件" : claim_subType_code = "3" ? "申復"
唯一的缺點是可讀性非常糟糕
這兩個文檔幾乎一模一樣,
唯一的差別在$uuid(),是用來生成一組隨機的UUID字串
numeric
JSONata支援+-*/%,要注意的地方是在array列舉時可以使用..(range)
[1..$count(Items)].("Item " & $) => ["Item 1","Item 2","Item 3"]
↑ 輸出從1~Items的個數次的 ("Item " & $)
這裡的$對應到的是[1..$count(Items)]的數值,"."代表的是Map操作,所以這行程式會把Item的個數以編號輸出成string array。
dateTime
JSONata對於時間控制的方法是把DateTime轉成millisecond,因此在量化操作時間時,
得先把DateTime格式轉換成純數字millisecond,接著運算完再對應回去。
舉例:
* effectiveDateTime = $fromMillis($toMillis($now()) + day * 86400000,'[Y0001]-[M01]-[D01]')
把effectiveDateTime這個FHIR屬性項,指派為 $now() + 輸入項day的數量 * 86400000(ms)的毫秒值,
再以'[Y0001]-[M01]-[D01]'的格式將毫秒值轉換回DateTime
今天我有一個coding欄位為非必填,而coding欄位又下分slices為system與code,
假設實作者綁死system的定義,寫出了以下的FUME Mapping:#FUME * coding * system = $loinc * code = input_code
而當input_code不存在於輸入端時,這時候轉換的結果會變成:#FHIR "coding" : [ { "system" : "http://loinc.org" } ]
想當然爾這個轉換結果是正確的,但不是我們要的
這個時候就會需要$exists()來判斷,我們想要達成的目標是,
當使用者不輸入input_code的時候,整個coding都不要被轉換出來
所以把coding改寫一下:
#FUME
* coding
* system = $exists(input_code) ? $loinc
* code = input_code
當input_code不存在時,system與code都不會對應出值,coding自然也就不會被轉換了。
#FUME
$array := [
"1",
"2",
"3",
"4"
]
* valueInteger = (input_value in $array) ? (TRUE) : (FALSE)
```
另外是array的使用方式,可以使用[start..end]來讀取
7. Path Operators
<https://docs.jsonata.org/processing>
這一步部分會比較抽象一點,直接看文件測試用法比較快。
8. Other Operators
這裡要講的除了Conditional Statement外,
前面使用過的 A & B -> 將A和B字串相連接,效果近似的還有$join(),不過$join可以使用的彈性會再更大一點,就見仁見智
另外就是Chain,算是一種JSONata的語法糖, 以~>表示
舉例:
```
#FUME
$uppercase($substringBefore($substringAfter(Customer.Email, "@"), "."))
```
等同於
```
Customer.Email ~> $substringAfter("@") ~> $substringBefore(".") ~> $uppercase()
```
其實大多數的內容筆者認為看這兩個Documentation加上實際在Playground測試即可掌握,
因為這裡的內容實在有點太大了,很難全部講的全,
比較好的狀況還是在實作中再來講。
明天講一些JSONata相對進階的操作,這部分滿不好講的,關於FHIR Server連動的部分則會放到後天。